package top.jacktgq.candychat.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;
import org.springframework.stereotype.Component;

@Component
public class WebSocketServer {
    // 使用静态内部类的单例模式
    private static class SingletoWebSocketServer {
        static final WebSocketServer INSTANCE = new WebSocketServer();
    }

    public static WebSocketServer getInstance() {
        return SingletoWebSocketServer.INSTANCE;
    }

    private EventLoopGroup bossGroup;
    private EventLoopGroup workerGroup;
    private ServerBootstrap bootstrap;
    private ChannelFuture future;
    private Channel channel;

    // 私有构造方法，防止被直接new出来
    private WebSocketServer() {
        bossGroup = new NioEventLoopGroup();
        workerGroup = new NioEventLoopGroup();

        bootstrap = new ServerBootstrap();

        bootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<Channel>() {
                    @Override
                    // 消息读入经过的handler顺序是：HttpServerCodec -> chunkedWriteHandler -> httpObjectAggregator -> IdleStateHandler -> HeartBeatHandler -> WebSocketServerProtocolHandler -> ChatHandler
                    // 消息写出经过的handler顺序是：WebSocketServerProtocolHandler -> IdleStateHandler -> ChunkedWriteHandler -> HttpServerCodec
                    protected void initChannel(Channel ch) throws Exception {
                        // WebSocket基于Http协议，所以要有Http编解码器
                        ChannelPipeline pipeline = ch.pipeline();
                        // 入 + 出
                        pipeline.addLast(new HttpServerCodec());
                        // 对写大数据流的支持
                        // 入 + 出
                        pipeline.addLast(new ChunkedWriteHandler());
                        // 对HttpMessage进行聚合，聚合成FullHttpRequest或FullHTTPResponse
                        // 几乎在基于Netty的WebSocket的编程中都会用到此Handler
                        // 入
                        pipeline.addLast(new HttpObjectAggregator(8192));

                        //================以上Handler是用于支持Http协议的Handler=================

                        //=====================增加心跳支持的Handler START=======================
                        // 如果客户端在12秒钟没有向服务器发送读写心跳，则主动断开，读写空闲不处理
                        // 入 + 出
                        // pipeline.addLast(new IdleStateHandler(8, 10, 12));
                        // 入
                        // pipeline.addLast(new HeartBeatHandler());
                        //=====================增加心跳支持的Handler END=========================

                        //================以下Handler是用于支持WebSocket协议的Handler==================
                        /**
                          * 1、对应WebSocket，它的数据是以帧（frame）形式进行传递的
                            2、可以看到WebSocketFrame下面有6个子类
                            3、浏览器发送请求时，ws://localhost:7000/xxx 表示请求的uri
                            4、WebSocketServerProtocolHandler 核心功能是将http协议升级为ws协议，保持长连接
                            5、是通过一个状态码 101将协议进行升级WebSocket服务器处理的协议，
                               用于指定给客户端连接访问的路由：/ws
                              本Handler会帮你处理一些繁重复杂的事：
                             （1）会帮你处理握手动作：handshaking(close, ping, pong) ping + pong = 心跳
                             （2）对应WebSocket来讲，都是以frame进行传输的，不同的数据类型对应的frame也不同。
                          */
                        // 入 + 出
                        pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));

                        // 自定义Handler
                        // 入
                        pipeline.addLast(new ChatHandler());
                    }
                });
    }

    public void start() {
        future = bootstrap.bind(8888);
        channel = future.channel();
        System.err.println("Netty WebSocket Server启动完毕...");
    }

    public void sendMsg(String msg) {
        channel.writeAndFlush(msg);
    }

    public void close() {

    }
}
